package main

import (
	"capnproto.org/go/capnp/v3"
	"capnproto.org/go/capnp/v3/rpc"
	"context"
	"fmt"
	books "gin-capnprotodemo/book"
	"gitlab.com/greyxor/slogor"
	"io"
	"log"
	"log/slog"
	"net"
	"net/http"
	"os"
	"time"

	"github.com/gin-gonic/gin"
)

func main() {
	// init log
	slog.SetDefault(slog.New(slogor.NewHandler(os.Stderr, &slogor.Options{
		TimeFormat: time.Stamp,
		Level:      slog.LevelDebug,
		ShowSource: true,
	})))

	// init rpc server
	l, err := net.Listen("tcp", "0.0.0.0:7001")
	chkfatal(err)
	defer l.Close()
	go func() {
		ctx := context.Background()
		conn, err := l.Accept()
		chkfatal(err)
		rpcServer(ctx, conn)
	}()

	dial, err := net.Dial("tcp", "0.0.0.0:7001")
	chkfatal(err)
	conn := rpc.NewConn(rpc.NewStreamTransport(dial), nil)
	defer conn.Close()
	bc := books.DemoService(conn.Bootstrap(context.Background()))

	// init web server
	r := gin.Default()
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"message": "pong",
		})
	})
	r.GET("/foo", func(c *gin.Context) {
		ctx := c.Request.Context()
		fooFutrue, free := bc.Foo(ctx, func(params books.DemoService_foo_Params) error {
			book, err := params.NewBook()
			if err != nil {
				return err
			}
			book.SetTitle("xxx")
			book.SetPageCount(100)
			return params.SetBook(book)
		})
		defer free()
		results, err := fooFutrue.Struct()
		if err != nil {
			c.JSON(http.StatusOK, gin.H{
				"error": err,
			})
			return
		}
		res, _ := results.Res()
		title, _ := res.Title()
		log.Println(fmt.Sprintf("set res: { title %s, page: %d }", title, res.PageCount()))
		c.JSON(http.StatusOK, gin.H{
			"data": gin.H{
				"title":     title,
				"pageCount": res.PageCount(),
			},
			"hasRes": results.HasRes(),
		})
	})
	r.GET("/sum", func(c *gin.Context) {
		ctx := c.Request.Context()
		sumResultsFuture, free := bc.Sum(ctx, func(params books.DemoService_sum_Params) error {
			params.SetA(1)
			params.SetB(2)
			return nil
		})
		defer free()
		results, err := sumResultsFuture.Struct()
		if err != nil {
			c.JSON(http.StatusOK, gin.H{
				"error": err,
			})
			return
		}
		sum := results.Sum()
		c.JSON(http.StatusOK, gin.H{
			"sum": sum,
		})
	})
	r.Run("0.0.0.0:8080")
}

func chkfatal(err error) {
	if err != nil {
		panic(err)
	}
}

type CapnpServer struct{}

func (c CapnpServer) Sum(_ context.Context, call books.DemoService_sum) error {
	log.Println("sum rpc handler called")
	args := call.Args()
	sum := args.A() + args.B()
	log.Println(fmt.Sprintf("get args: {a: %d, b: %d }", args.A(), args.B()))
	results, err := call.AllocResults()
	if err != nil {
		return err
	}
	results.SetSum(sum)
	return nil
}

var _ books.DemoService_Server = CapnpServer{}

func (c CapnpServer) Foo(_ context.Context, call books.DemoService_foo) error {
	log.Println("foo rpc handler called")
	args := call.Args()
	book, _ := args.Book()
	title, _ := book.Title()
	log.Println(fmt.Sprintf("get args: { title: %s, page: %d }", title, book.PageCount()))
	res, err := call.AllocResults()
	if err != nil {
		return err
	}
	return res.SetRes(book)
}

func rpcServer(ctx context.Context, rwc io.ReadWriteCloser) error {
	cli := books.DemoService_ServerToClient(CapnpServer{})
	conn := rpc.NewConn(rpc.NewStreamTransport(rwc), &rpc.Options{
		BootstrapClient: capnp.Client(cli),
	})
	defer conn.Close()

	select {
	case <-conn.Done():
		return nil
	case <-ctx.Done():
		return conn.Close()
	}
}
